package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"os"
	"time"
)

var (
	zprefix = "zrange:"
	store   *redis.Pool
)

func initStore() {
	if store != nil {
		return
	}
	// host := "10.153.90.12"
	host := "10.153.90.5"
	port := "6379"
	//store = redis.NewPool(func() (redis.Conn, error) {
	//	return redis.Dial("tcp", host+":"+port)
	//}, 5)

	store = &redis.Pool{Dial: func() (redis.Conn, error) {
		return redis.Dial("tcp", host+":"+port, redis.DialPassword("L0v1!@#$"), redis.DialDatabase(6)) // redis.DialUsername("root"), 带用户名连接出错.
	}, MaxIdle: 5}
}

func Exists(namespace, key string) (bool, error) {
	initStore()
	conn := store.Get()
	defer conn.Close()
	return redis.Bool(conn.Do("HEXISTS", namespace, key))
}

func Get(namespace, key string) ([]byte, error) {
	initStore()
	conn := store.Get()
	defer conn.Close()
	b, err := redis.Bytes(conn.Do("HGET", namespace, key))
	if err == redis.ErrNil {
		return nil, errors.New("Not found! ")
	}
	return b, err
}

// HSET KEY_NAME FIELD VALUE
// zset使用：https://www.runoob.com/redis/redis-sorted-sets.html
func Put(namespace, key string, value []byte) error {
	initStore()
	conn := store.Get()
	defer conn.Close()

	_, err := conn.Do("ZADD", zprefix+namespace, time.Now().UnixNano(), key)
	if err != nil {
		return err
	}
	_, err = conn.Do("HSET", namespace, key, value)
	return err
}

func Del(namespace, key string) error {
	initStore()
	conn := store.Get()
	defer conn.Close()

	_, err := conn.Do("ZREM", zprefix+namespace, key)
	if err != nil {
		return err
	}
	_, err = conn.Do("HDEL", namespace, key)
	return err
}

func Range(namespace string, offset, limit int) ([][]byte, error) {
	// lookup zrange
	// get keys with hmget
	initStore()
	conn := store.Get()
	defer conn.Close()

	keys, err := redis.Strings(conn.Do("ZREVRANGE", zprefix+namespace, offset, limit))
	if err != nil {
		return nil, err
	}

	if len(keys) == 0 {
		return [][]byte{}, nil
	}

	args := []interface{}{namespace}
	for _, key := range keys {
		args = append(args, key)
	}

	result, err := redis.Strings(conn.Do("HMGET", args...))
	if err != nil {
		return nil, err
	}

	var results [][]byte
	for _, res := range result {
		results = append(results, []byte(res))
	}

	return results, nil
}

// Examples:
// https://pkg.go.dev/github.com/gomodule/redigo/redis#pkg-examples
// 和go-redis比较优劣性：https://github.com/go-redis/redis.
func main() {
	//host := "10.153.90.12"
	//port := "6379"
	//redisStore, err := redis.Dial("tcp", host+":"+port, redis.DialPassword("L0v1!@#$"),  redis.DialDatabase(6)) // redis.DialUsername("root"),
	//if err != nil {
	//	fmt.Println(err)
	//	os.Exit(-1)
	//}
	//
	//replySet, err := redisStore.Do("SET", "key1", "value1")
	//if err != nil {
	//	fmt.Println("SET error: ", err)
	//}
	//fmt.Println(replySet)
	initStore()
	type Student struct {
		Id   int    `binding:"required"`
		Name string `binding:"required"`
	}
	var student Student
	student.Id = 123
	student.Name = "amsterdam"
	body, err := json.Marshal(&student)
	fmt.Println(string(body))
	err = Put("myhash", "fieldl1", body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
	err = Put("myhash2", "fieldl1", body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	var student2 Student
	student2.Id = 124
	student2.Name = "beijing"
	body, _ = json.Marshal(student2)
	fmt.Println(string(body))
	err = Put("myhash", "fieldl2", body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-2)
	}
	err = Put("myhash2", "fieldl2", body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-2)
	}

	content, err := Get("myhash", "fieldl1")
	if err != nil {
		fmt.Println(err)
		os.Exit(-3)
	}
	var studentRes Student
	err = json.Unmarshal(content, &studentRes)
	if err != nil {
		fmt.Println(err)
		os.Exit(-4)
	}
	fmt.Println(studentRes)

	contest, err := Range("myhash2", 0, -1)
	if err != nil {
		fmt.Println(err)
		os.Exit(-5)
	}
	fmt.Println("range result")
	for _, tem := range contest {
		var studentTmp Student
		err = json.Unmarshal(tem, &studentTmp)
		if err != nil {
			fmt.Println(err)
			os.Exit(-4)
		}
		fmt.Println(studentTmp)
	}
}
